<!-- 文章浏览 -->
<template>
  <div ref="detailContent" id="detail-view" class="detail-view" @click.stop="detailViewSidebarClickHandler(0)">
    <CommonHeaderToolBar/>
    <!-- detail 内容 -->
    <div class="grid p-6 detail-container">
      <!-- 文字内容 -->
      <div id="detail-content" class="col-12">
        <Skeleton v-if="detailLoading" height="10vh" width="60vw" class="mb-5"></Skeleton>
        <div id="detailTitle" v-else class="detail-title mb-5">
          {{ detailEntity.title }}
        </div>
        <Skeleton v-if="detailLoading" height="65vh" width="80vw" class="mb-5"></Skeleton>
        <!--        <div v-show="!detailLoading" class="vditor-reset" id="vditorPreview"></div>-->
        <MdPreviewPanel v-show="!detailLoading" :markdown="detailEntity.content" @after="findHDom"/>
      </div>
      <!-- 评论区 -->
      <div ref="commentSection" class="col-12 border-top-1 border-500 p-5">
        <div class="grid">
          <!-- 头部 -->
          <div class="col-7 text-right">
            <span class="segoe__print__family" style="font-size: 2rem;">
              <span class="text-red-600">
                ({{ detailEntity.commentNumber }})
              </span>
               Comment Section
            </span>
          </div>
          <!-- 排序 -->
          <div class="flex col-5 text-right justify-content-end align-items-end align-content-end">
            <div class="comment__sort__item comment__sort__item__active" @click="commentSort(0, $event)">
              最新
            </div>
            <div class="comment__sort__item" @click="commentSort(1, $event)">
              最热
            </div>
            <div class="comment__sort__item" @click="commentSort(2, $event)">
              最早
            </div>
          </div>
          <!-- 评论内容 -->
          <div class="col-12 p-4">
            <RecursionPanel :list="commentEntities"
                            parent-class="mt-5 border-bottom-1 "
                            child-class="pl-3 pt-3"
                            child-style="margin-top: -20px;"
                            child="detailCommentVos">
              <template #container="{ item, extend }">
                <div class="grid">
                  <!-- 用户信息 -->
                  <div class="col-6 flex">
                    <div class="flex-initial">
                      <ImgTextAvatar :label="item.nickname" :image="item.avatar"></ImgTextAvatar>
                    </div>
                    <div class="flex align-content-end align-items-end pb-1 pl-2 comment__user-info"
                         :style="`${extend ? 'font-weight: 500;' : 'font-weight: 700;'}`">
                      <a href="javascript:">{{ item.nickname }}</a>
                    </div>
                  </div>
                  <!-- 评论时间 -->
                  <div class="col-6">
                    <div class="flex justify-content-end align-content-end align-items-end">
                   <span class="comment__opera__item">
                      {{ item.createTime }}
                   </span>
                    </div>
                  </div>
                </div>
                <!-- 评论内容 -->
                <div class="p-2 comment__preview">
                  <MdPreviewPanel :markdown="decodeURIComponent(decrypt(item.content))"></MdPreviewPanel>
                </div>
                <!-- 操作评论 -->
                <div class="text-right pb-2">
                  <span class="comment__opera__item" @click="openReply(item.id, item.nickname)">
                    <a href="javascript:">
                      回复 ({{ item.follow }})
                    </a>
                  </span>
                  <span class="comment__opera__item" @click="kudosComment(item.id, item.toGood)">
                    <a href="javascript:">
                      点赞 ({{ item.goodNumber }})
                    </a>
                  </span>
                </div>
              </template>
            </RecursionPanel>
          </div>
        </div>

      </div>
    </div>
    <!-- 锚点 -->
    <AffixPanel :top="`${detailButtonTop}px`" right="1px" :dom="detailContent">
      <template #content>
        <Button icon="pi pi-cog"
                class="sidebar-button"
                ref="sidebarButton"
                :style="sidebarRunStyle"
                @click.stop="detailViewSidebarClickHandler(1)"/>
      </template>
    </AffixPanel>
    <!-- 工具栏 -->
    <Sidebar v-model:visible="visibleSidebar"
             id="sidebar"
             ref="sidebar"
             position="right"
             :baseZIndex="10000"
             :modal="false"
             :showCloseIcon="false">
      <!-- 介绍作者 -->
      <div class="p-tree border-bottom-none"
           style="border-bottom-right-radius: 0 !important; border-bottom-left-radius: 0 !important">
        <h3 style="margin-block-start: 0;">作者介绍</h3>
        <!-- 作者信息 -->
        <div class="flex">
          <div class="flex justify-content-start" style="cursor: pointer;">
            <Avatar :image="detailEntity.avatar"></Avatar>
            <span class="ml-2 flex justify-content-center align-items-center align-content-center">{{
                detailEntity.author
              }}</span>
            <span class="ml-3 flex justify-content-center align-items-center align-content-center">
               <Button v-show="userMsg && userMsg.nickname !== detailEntity.author"
                       :icon="authorUserMsg.fans ? 'pi pi-bookmark-fill' : 'pi pi-bookmark'"
                       :loading="focusHandlerLoading"
                       :label="authorUserMsg.fans ? '取消关注' : '关注'"
                       @click="focusHandler($event)"
                       class="p-button-rounded p-button-secondary p-button-text p-2"/>
            </span>

          </div>
        </div>
        <div class="mt-2">
          <span style="font-size: 14px; color: var(--gray-500)">{{ detailEntity.authorDesc }}</span>
        </div>
        <!-- 操作按钮 -->
        <div class="flex mt-2 justify-content-start">
          <!-- 收藏 -->
          <Button :icon="detailEntity.toCollection ? 'pi pi-heart-fill' : 'pi pi-heart'"
                  :label="detailEntity.collectionNumber + ''"
                  :style="detailEntity.toCollection ? 'color: red' : ''"
                  @click="collectionDetail"
                  class="p-button-rounded p-button-secondary p-button-text p-2"/>
          <!-- 点赞 -->
          <Button :icon="detailEntity.toGood ? 'pi pi-thumbs-up-fill' : 'pi pi-thumbs-up'"
                  :label="detailEntity.goodNumber + ''"
                  :style="detailEntity.toGood ? 'color: red' : ''"
                  @click="kudosDetail"
                  class="p-button-rounded p-button-secondary p-button-text p-2 transition-all"/>
        </div>
      </div>
      <!-- Tree 生成树 -->
      <Tree v-if="HDomArray.length > 0"
            style="border-top-right-radius: 0 !important; border-top-left-radius: 0 !important"
            :value="HDomArray"
            class="detail-list border-top-none">
        <!-- 目录 -->
        <template #url="slotProps">
          <a href="javascript:" @click="goNode(slotProps.node.data)">{{ slotProps.node.label }}</a>
        </template>
        <!-- 专栏 -->
        <template #special="slotProps">
          <div class="grid">
            <div class="col-12">
              <a href="javascript:" @click="getToDetail(slotProps.node.label.id)">{{ slotProps.node.label.title }}</a>
              <div class="detail-desc m-text-overflow-ellipsis-2">
                {{ slotProps.node.label.desc }}
              </div>
            </div>
          </div>
        </template>
        <!-- 评论 -->
        <template #comment="slotProps">
          <span class="tree__opera__item" @click="commentOpera(slotProps.node.level)">
            {{ slotProps.node.label }}
          </span>
        </template>
      </Tree>
    </Sidebar>
    <!-- 评论编辑器 -->
    <EditorDialog class="comment__editor"
                  style="width: 50vw;"
                  img-default-height="100px"
                  img-default-width="100px"
                  img-max-width="200px"
                  img-max-height="200x"
                  dialog-position="top"
                  editor-place-holder="在这里发表下你的意见吧，支持采用 markdown 语法哦~!"
                  @close="commentDialog = false"
                  @editor-change="editorChangeHandler"
                  :show="commentDialog"
                  :toolbar="[]"
                  :show-input="false"
                  :outline="false">
      <template #header>
        <div v-if="openMode === 0" style="font-weight: 600; font-size: 1.1rem;">
          <span>评论</span>
          <span class="text-red-400" style="font-weight: 600;">{{ detailEntity.title }}</span>
          <span>一手</span>
        </div>
        <div v-else>
          <span>回复一手 </span>
          <span class="text-red-400" style="font-weight: 600;">{{ tempCommentUser }}</span>
        </div>
      </template>
      <template #footer>
        <div class="flex justify-content-end">
          <Button @click="reply()"
                  label="评论"></Button>
        </div>
      </template>
    </EditorDialog>
  </div>
</template>

<script setup lang="ts">
/** JS */
import {onMounted, reactive, ref, watch, Ref, nextTick} from 'vue';
import {
  CommentService,
  DetailCommentInfo,
  ResponseResult,
  KudosComment,
  UserService,
  UserMsg,
  UserMsgUpdateInfo, UserOperationDetail
} from '@/utils/ApiRequest';
import {DetailResponseData, DetailSpecial} from '@/interface/detail/entity/DetailEntityInterface';
import {DetailService, DetailComments} from '@/utils/ApiRequest';
import {decrypt, encrypt} from '@/utils/PakoUtils';
import {useRoute, useRouter} from 'vue-router';
import toast from '@/utils/Toast';
import {ExecFunction} from '@/utils/YzjUtils';
import Cache from '@/utils/Cache';
/** 组件 */
import CommonHeaderToolBar from '@/components/toolbar/CommonHeaderToolBar.vue';
import RecursionPanel from '@/components/panel/RecursionPanel.vue';
import MdPreviewPanel from '@/components/panel/MdPreviewPanel.vue';
import AffixPanel from '@/components/panel/AffixPanel.vue';
import EditorDialog from '@/components/editor/Dialog/EditorDialog.vue';
import ImgTextAvatar from '@/components/avatar/ImgTextAvatar.vue';
import Skeleton from 'primevue/skeleton';
import Tree from 'primevue/tree';
import Sidebar from 'primevue/sidebar';


/** 选项接口 */
interface HDom {
  key: string,
  label: any,
  level: number,
  data?: any,
  icon?: string,
  children?: HDom[],
  prev?: HDom,
  type?: string,
}


const detailService = new DetailService(),
    commentService = new CommentService(),
    userService = new UserService(),
    route = useRoute(),
    router = useRouter(),
    cache = new Cache(),
    userMsg: UserMsg = cache.get('userMsg'),
    execFunction = {
      comment: {
        kudos: new ExecFunction(),
        reply: new ExecFunction()
      },
      author: {
        focus: new ExecFunction()
      },
      detail: {
        kudos: new ExecFunction(),
        collection: new ExecFunction()
      }
    };
// execFunction = new ExecFunction();
let id = Number(route.params.id);
/** 文章数据对象 */
let detailEntity: DetailResponseData = reactive({
  /** 作者 */
  author: '',
  /** 是否格式化 */
  autoFormat: false,
  /** 作者头像 */
  avatar: '',
  /** 作者简介 */
  authorDesc: '',
  /** 综合评分 */
  aveRate: 0,
  /** 收藏数量 */
  collectionNumber: 0,
  /** 评论数量 */
  commentNumber: 0,
  /** 文本内容需要 gzip 解压 */
  content: '',
  /** 简介 */
  desc: '',
  /** 点赞数量 */
  goodNumber: 0,
  /** 文章 id */
  id: 0,
  /** 发布时间 */
  publishTime: '',
  /** 文章序列号 */
  serialNumber: 0,
  /** 文章专栏 */
  special: '',
  /** 文章标签 */
  tags: '',
  /** 是否为草稿 */
  tempSave: false,
  /** 文章标题 */
  title: '',
  /** 用户是否收藏 */
  toCollection: false,
  /** 用户是否评论 */
  toComment: false,
  /** 用户是否点赞 */
  toGood: false,
  /** 用户是否评分 */
  toRate: 0,
  /** 发布文章的作者 id */
  userId: 0,
  /** 文章浏览数量 */
  view: 0,
  /** 专栏对象 */
  specialVOS: [] as DetailSpecial[]
});
/** 评论数据对象 */
let commentEntities: Ref<DetailComments[]> = ref([]);
/** 文章加载 */
let detailLoading = ref(true);
/** 元素钩子 */
let detailContent = ref(null), sidebar = ref(null), sidebarButton = ref(null);
/** 动态适应性 px 高度 */
let detailButtonTop = ref(0);
/** Sidebar 显隐以及按钮动画 */
let visibleSidebar = ref(false), saveWidth = ref(0), sidebarRunStyle = ref('');
const detailViewSidebarClickHandler = (mode: number) => {
      if (mode === 0) {
        if (visibleSidebar.value) {
          visibleSidebar.value = false;
        }
      } else {
        visibleSidebar.value = !visibleSidebar.value;
      }
    },
    sidebarButtonAnimateHandler = (judge: boolean) => {
      const openClass = 'sidebar-cog-720', closeClass = 'sidebar-cog-0';
      let buttonEl = (sidebarButton.value as any).$el;
      let classList = buttonEl.children[0].classList;
      if (judge) {
        if (classList.contains(closeClass)) {
          buttonEl.children[0].classList.remove(closeClass);
        }
        buttonEl.children[0].classList.add(openClass);
      } else {
        if (classList.contains(openClass)) {
          buttonEl.children[0].classList.remove(openClass);
        }
        buttonEl.children[0].classList.add(closeClass);
      }
    }
watch(() => visibleSidebar.value, (n, o) => {
  // 需要加 nextTick 因为 sidebar 是 if 动态渲染，需要在 sidebar 渲染完毕后执行
  nextTick(() => {
    if (n) {
      let doc = document.getElementById('sidebar');
      if (doc) {
        let width = doc.clientWidth;
        if (sidebar.value) {
          sidebarRunStyle.value = `transform: translateX(-${width}px)`
          saveWidth.value = width;
        }
      }
    } else {
      sidebarRunStyle.value = `transform: translateX(1px);`
    }
    sidebarButtonAnimateHandler(n);
  });
})

/** ------------------------ 工具 sidebar 的构造 ------------------------*/
/** 递归找标题 */
let HDomArray: Ref<HDom[]> = ref([]), HS = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'];
let tempPrevHDom: HDom | undefined;
const findHDom = (dom: any) => {
  // 初始化数组
  HDomArray.value = [];

  /** 构建专栏 */
  function buildSpecial() {
    let specialVos = detailEntity.specialVOS;
    // 有相关存在的专栏列表
    if (specialVos && specialVos.length > 0) {
      let hdom = {
        icon: 'pi pi-book',
        key: '-2',
        label: '相关专栏',
        level: -2,
        children: [] as HDom[]
      }
      let children: HDom[] = [];
      for (let i = 0; i < specialVos.length; i++) {
        let vo = specialVos[i];
        if (vo.title === detailEntity.title) continue;
        children.push({
          type: 'special',
          key: i * -1 + '',
          label: {id: vo.id, title: vo.title, desc: vo.desc},
          level: i,
        })
      }
      hdom.children = children;
      HDomArray.value.push(hdom);
    }
  }

  /** 构建评论区 */
  function buildComment() {
    let hdom = {
      icon: 'pi pi-comment',
      key: '-3',
      label: '评论',
      level: -3,
      children: [
        {
          type: 'comment',
          key: '-3-1',
          label: '评论一手',
          level: 0,
        },
        {
          type: 'comment',
          key: '-3-2',
          label: '跳至评论区',
          level: 1,
        }] as HDom[]
    }
    HDomArray.value.push(hdom);
  }

  /**
   * 构建双向链表整合数据
   *
   * <ul>通过计算等级差与 target 进行比较，并记录临时的插入点从而实现双向链表
   *    <li>如果 target > 0 则查找层级差与 target 相同的而后在 child 插入 dom
   *    <li>如果 target === 0 说明层级与上次的相同，直接在临时插入点再次插入
   *    <li>如果 target < 0 则根据层级差往上遍历差的值并将其 prev 作为临时插入点
   * */
  const recursiveBuildDomArray = (rootHDom: HDom[], currentHDom: HDom, target: number) => {
        if (rootHDom.length === 0) {
          rootHDom.push(currentHDom);
        }
        let tempHDom = rootHDom[rootHDom.length - 1],
            // 计算等级差
            difference = currentHDom.level - tempHDom.level, child;
        // console.log('3. rootHDom = ', rootHDom);
        // console.log('4. currentHDom = ', currentHDom);
        // console.log('5. tempHDom = ', tempHDom)
        // console.log('6. target = ', target);
        // 等级的相差不相符。current 必然比 root 中的层级大或者相同【除非循序是，第一个任意等级 2,1 ... --> 目录 level 采用了动态，确保了必定比文章第一个 H 标签的优先级要高 2022/11/17 10:22】
        if ((child = tempHDom.children)) {
          if (target > 0 && difference === target) {
            // target != 0 直接插入
            tempHDom.children.push(currentHDom);
            currentHDom.prev = tempHDom;
            // 记录插入的数据
            tempPrevHDom = tempHDom;
          } else if (target === 0) {
            // 插入与上次相同的位置
            if (tempPrevHDom) {
              tempPrevHDom.children?.push(currentHDom);
              currentHDom.prev = tempPrevHDom;
            } else {
              tempHDom.children?.push(currentHDom);
              currentHDom.prev = tempHDom;
            }
          } else if (target < 0) {
            // 负数时往上查找节点 parent
            let prev: HDom | undefined, tempTarget = -1 * target;
            // 往上找 |target| 次
            for (let j = 0; j < tempTarget; j++) {
              if (tempPrevHDom?.prev) {
                tempPrevHDom = tempPrevHDom.prev;
                prev = tempPrevHDom as HDom;
              } else {
                // 如果上一级没有 prev 我们就认为它的父级就是目录
                prev = tempHDom;
              }
            }
            if (prev?.children) {
              prev.children.push(currentHDom);
            }
            currentHDom.prev = prev;
          } else {
            recursiveBuildDomArray(child, currentHDom, target);
          }
        }
      },
      /** vue 渲染不支持双向链表，所以需要清除 prev */
      recursiveClearDomArray = (hdoms: HDom[]) => {
        // 清除 prev 印记
        if (hdoms && hdoms.length > 0) {
          for (let HDomArr of hdoms) {
            HDomArr.prev = undefined;
            if (HDomArr.children && HDomArr.children.length > 0) {
              recursiveClearDomArray(HDomArr.children);
            }
          }
        }
      };
  /** 构建目录 */
  let currentHDom!: HDom, index = 0, prevLevel = 0;
  for (let child of dom.children) {
    let nodeName;
    // 判断是否为标题标签
    if (HS.indexOf((nodeName = child.nodeName)) !== -1) {
      let HLevel = Number(nodeName.replace('H', ''));
      // console.log('0. HLevel = ', HLevel)
      currentHDom = {
        icon: 'pi pi-link',
        key: index + '',
        label: child.innerText,
        level: HLevel,
        children: [],
        type: 'url',
        data: `${child.getAttribute('id')}`
      }
      if (prevLevel === 0) {
        prevLevel = HLevel;
      }
      // 第一个直接 push 不需要走复杂判断
      if (index === 0) {
        HDomArray.value.push({
          icon: 'pi pi-bookmark',
          key: '-1',
          label: '目录',
          level: prevLevel,
          children: [currentHDom],
          type: 'url',
          data: 'detailTitle'
        })
      } else {
        // console.log('1. HLevel = ', HLevel)
        // console.log('2. prevLevel = ', prevLevel)
        recursiveBuildDomArray(HDomArray.value, currentHDom, HLevel - prevLevel);
        prevLevel = HLevel;
      }
      index++;
    }
  }
  // 清除 prev 将清除链表双向
  recursiveClearDomArray(HDomArray.value);
  // 追加栏
  buildSpecial();
  buildComment();
}

/** 控制评论编辑器参数属性*/
const commentDialog = ref(false),
    /** openMode === 0 时评论文章， openMode === 1 时回复评论 */
    openMode = ref(0),
    commentSection = ref(null),
    replyCommentInfo: DetailCommentInfo = reactive({
      detailId: id,
      content: ''
    }),
    tempCommentUser = ref(''),
    /** 作者信息 */
    authorUserMsg: Ref<UserMsg> = ref({
      /** 昵称 */
      nickname: detailEntity.author,
      /** 头像 */
      avatar: detailEntity.avatar,
      /** 用户简介 */
      desc: detailEntity.authorDesc,
      username: '',
      /** 电子邮箱 */
      email: '',
      /** 等级 */
      level: 0,
      /** 是否关注 */
      focus: false,
      /** 粉丝数量 */
      fansCount: 0
    });

function addAnimated(ev: PointerEvent, timeout: number, ...animates: string[]) {
  let evPath = ev.composedPath()
  for (let eventTarget of evPath) {
    let element = eventTarget as HTMLElement;
    if (element.nodeName === 'BUTTON') {
      if (animates.length > 0) {
        element.classList.add('animate__animated')
        for (let animate of animates) {
          element.classList.add(animate);
        }
      }
      // element.classList.add('animate__animated', 'animate__heartBeat', 'animate__faster');
      setTimeout(() => {
        if (animates.length > 0) {
          element.classList.remove('animate__animated')
          for (let animate of animates) {
            element.classList.remove(animate);
          }
        }
      }, timeout);
      break;
    }
  }
}
/** 关注操作 loading */
const focusHandlerLoading = ref(false);
/** 回复评论 */
const openReply = (id: number, name: string) => {
      openMode.value = 1;
      commentDialog.value = true;
      tempCommentUser.value = name;
      // 赋予 parentCommentId
      replyCommentInfo.parentCommentId = id;
    },
    /** 监听编辑器 */
    editorChangeHandler = (val) => {
      replyCommentInfo.content = val.md;
    }
/** 跳转至导航 */
const goNode = (dom: any) => {
      if (dom) {
        let element: HTMLElement | null = document.getElementById(dom);
        element?.scrollIntoView({
          behavior: "smooth",  // 平滑过渡
          block: "start"
        })
      }
    },
    /** 跳转至指定文章【重渲染】 */
    getToDetail = (id) => {
      router.push({
        path: '/detail/' + id
      })

    },
    /** 获取文章 */
    getDetail = async (id) => {
      detailLoading.value = true;
      try {
        let res: ResponseResult<DetailResponseData> = await detailService.getDetailById({id: id, tempSave: false});
        Object.assign(detailEntity, res.data);
        detailEntity.content = decodeURIComponent(decrypt(res.data.content));
        document.title = detailEntity.title;
        detailLoading.value = false;
        document.documentElement.scrollTop = 0
        await getAuthorMsg();
      } catch (e) {
        setTimeout(() => {
          router.push({
            path: '/'
          });
        }, 1500);
      }
    },
    /** 获取评论 */
    getComments = async (id) => {
      let res: ResponseResult<DetailComments[]> | undefined = await commentService.detailComments(id);
      if (res && res.code === 0) {
        commentEntities.value = res.data;
      }
    },
    /**
     * 评论排序
     *
     * @param mode 默认 -- 0: 最新（createTime 最新）| 1:最热（点赞最多）| 2: 最早（createTime 最早）
     * */
    commentSort = (mode: number, ev: PointerEvent) => {
      let target = ev.target;
      if (target) {
        let element = target as HTMLElement;
        if (element.parentElement) {
          let siblings = element.parentElement.children;
          for (let sibling of siblings) {
            if (sibling === element) continue;
            if (sibling.classList.contains('comment__sort__item__active')) sibling.classList.remove('comment__sort__item__active');
          }
          element.classList.add('comment__sort__item__active')
        }
      }
      let sort = commentEntities.value;
      if (mode === 0 || mode === 2) {
        sort.sort((a, b) => {
          let aTime = new Date(a.createTime).getTime(), bTime = new Date(b.createTime).getTime();
          if (mode === 2) {
            return aTime - bTime
          } else {
            return bTime - aTime
          }
        })
      } else if (mode === 1) {
        sort.sort((a, b) => {
          let aGood = a.goodNumber, bGood = b.goodNumber;
          return bGood - aGood;
        })
      }
      commentEntities.value = sort;
    },
    /** 操作评论 */
    commentOpera = async (level: number) => {
      if (level === 0) {
        // 打开评论文章
        openMode.value = 0;
        commentDialog.value = true;
      } else if (level === 1) {
        // 跳转评论区
        if (commentSection.value) {
          const element = commentSection.value as HTMLElement;
          element.scrollIntoView({behavior: 'smooth', block: 'start', inline: 'start'})
        }
      }
    },
    /** 评论，如果 replyComment 为 true 则追加 parentCommentId */
    reply = async () => {
      if (replyCommentInfo.content.trim() === '') {
        toast.warn('打咩哟~', '灌水归灌水，起码来点内容吧！┗|｀O′|┛', 2000);
        return;
      }
      let data: DetailCommentInfo = {
        detailId: id,
        content: encrypt(replyCommentInfo.content)
      }
      if (openMode.value === 1) {
        data.parentCommentId = replyCommentInfo.parentCommentId;
      }

      // 记录回滚
      let record = await execFunction.comment.reply.record(detailEntity.commentNumber).exec(async () => {
        detailEntity.commentNumber += 1;
        let res = await commentService.relyComment(data);
        if (res.code === 0) {
          toast.success('感谢评论', "随着你这种热爱讨论的人愈来愈多，我相信论坛也会变得热闹起来呢~(●'◡'●)", 2000);
          await getComments(detailEntity.id);
        }
        // 关闭输入框
        commentDialog.value = false;
      });
      if (record) {
        detailEntity.commentNumber = record;
      }
    },
    /** 点赞评论 */
    kudosComment = async (id: number, toGood: boolean) => {
      let data: KudosComment = {
        commentId: id
      }
      execFunction.comment.kudos.blockageFunction(async () => {
        let entities = commentEntities.value;
        let record = await execFunction.comment.kudos.record(entities).exec(async () => {
          let findEnd = false;

          // 递归寻找
          function recursion(childComment: DetailComments[]) {
            if (childComment && childComment.length > 0) {
              // 存在子评论
              for (let comment of childComment) {
                if (comment.id === id) {
                  if (!toGood) {
                    comment.goodNumber += 1;
                  } else {
                    comment.goodNumber -= 1;
                  }
                  comment.toGood = !toGood;
                  findEnd = true;
                  return;
                } else {
                  recursion(comment.detailCommentVos);
                }
              }
            }
          }

          for (let commentEntity of commentEntities.value) {
            if (commentEntity.id === id) {
              if (!toGood) {
                commentEntity.goodNumber += 1;
              } else {
                commentEntity.goodNumber -= 1;
              }
              commentEntity.toGood = !toGood;
              findEnd = true;
              break;
            }
          }
          // 从顶级父类中没找到，从子级找
          if (!findEnd) {
            for (let commentEntity of commentEntities.value) {
              recursion(commentEntity.detailCommentVos);
            }
          }
          await commentService.kudosComment(data);
        });
        if (record) {
          commentEntities.value = entities;
        }
      }, (time) => toast.warn('心急吃不了热豆腐~(￣▽￣)~*', `请${(time / 1000).toFixed(2)}秒后重试`, 1500), 3000);
    },
    /** 获取当前用户与作者的关系 */
    getAuthorMsg = async () => {
      let res = await userService.getUserMsg(detailEntity.author);
      authorUserMsg.value = res.data;
    },
    /** 关注操作 */
    focusHandler = async (ev: PointerEvent) => {
      let record = await execFunction.author.focus.record(authorUserMsg.value.focus).blocking().exec(async () => {
        addAnimated(ev, 500, 'animate__animated', 'animate__heartBeat', 'animate__faster');
        setTimeout(() => {
          focusHandlerLoading.value = true;
        }, 300);
        let data: UserMsgUpdateInfo = {
          focus: authorUserMsg.value.nickname
        }
        let res = await userService.update(data);
        let lastFocus = res.data.toFocus;
        if (lastFocus) {
          toast.success('关注成功', `很高兴你能在这里找到喜欢的人呢~(￣▽￣)"`, 3000)
        } else {
          toast.warn('取关成功', `世上又多了一个失去粉丝的人呢~/(ㄒoㄒ)/~~`, 3000)
        }
        authorUserMsg.value.fans = !authorUserMsg.value.fans;
        focusHandlerLoading.value = false;
      }, (time) => {
        toast.error('等等..等等啊~~', `太快了太快了，请${(time / 1000).toFixed(2)}秒后重试。🤬`, 1500)
      }, 3000);
      // 回滚
      if (record) {
        authorUserMsg.value.focus = record;
        focusHandlerLoading.value = false;
      }
    },
    /** 收藏文章 */
    collectionDetail = async (ev) => {
      // 收藏
      let opera = !detailEntity.toCollection;
      let record = await execFunction.detail.collection.record(detailEntity).blocking().exec(async () => {
        addAnimated(ev, 500, 'animate__animated', 'animate__heartBeat', 'animate__faster');
        detailEntity.collectionNumber = opera ? detailEntity.collectionNumber + 1 : detailEntity.collectionNumber - 1;
        detailEntity.toCollection = (opera as boolean);
        await detailService.operationDetails(({detailId: detailEntity.id, toCollection: opera} as UserOperationDetail))
      }, (time) => {
        toast.error('等等..等等啊~~', `太快了太快了，请${(time / 1000).toFixed(2)}秒后重试。🤬`, 1500)
      }, 3000)
      if (record) {
        detailEntity = record;
      }
    },
    /** 点赞文章 */
    kudosDetail = async (ev) => {
      // 点赞
      let opera = !detailEntity.toGood;
      let record = await execFunction.detail.kudos.record(detailEntity).blocking().exec(async () => {
        addAnimated(ev, 500, 'animate__animated', 'animate__heartBeat', 'animate__faster');
        detailEntity.goodNumber = opera ? detailEntity.goodNumber + 1 : detailEntity.goodNumber - 1;
        detailEntity.toGood = (opera as boolean);
        await detailService.operationDetails(({detailId: detailEntity.id, toGood: opera} as UserOperationDetail))
      }, (time) => {
        toast.error('等等..等等啊~~', `太快了太快了，请${(time / 1000).toFixed(2)}秒后重试。🤬`, 1500)
      }, 3000)
      if (record) {
        detailEntity = record;
      }
    }
/** 监听路由变化实时发生改变 */
watch(() => router.currentRoute.value.params, (n) => {
  getDetail(n.id);
  getComments(n.id);
})
/** ----------------------------------------------------------------*/

/** ------------------------ onMounted 挂载 ------------------------*/
onMounted(async () => {
  await getDetail(id);
  await getComments(id);
  // 计算高度，客户端页面高度的 1/2
  if (detailContent.value) {
    /*    let htmlEle = detailContent.value as HTMLElement;
        console.log('document. documentElement. clientWidth = ', document.documentElement.clientWidth);
        console.log('htmlEle client height = ', htmlEle.clientHeight)*/
    detailButtonTop.value = Math.floor(document.documentElement.clientHeight / 2);
  }
})
/** ----------------------------------------------------------------*/
</script>


<style lang="less" scoped>
.detail-view {
  .detail-container {
    padding: 15px;
    background-color: #FDF5E6;
  }

  .detail-title {
    font-size: 40px !important;
  }
}

.detail-desc {
  color: rgb(59, 64, 69);
  font-size: 12px;
  margin-top: 5px;
}

.comment__user-info {
  &:hover {
    cursor: pointer;
    color: blue;
  }
}

.tree__opera__item {
  &:hover {
    color: dodgerblue;
    cursor: pointer;
  }

  transition: all .15s;
}

.comment__content {
  color: rgb(89, 89, 89);
}

.comment__opera__item {
  font-size: 12px;
  padding: 3px;
}

.comment__user-info {
  font-size: 16px;
  padding: 3px;
}

.comment__sort__item {
  padding: 5px;
  margin: 0 5px;
  background-color: #eeeeee;

  &:hover {
    cursor: pointer;
    background-color: red;
    color: white;
    font-weight: 600;
  }

  transition: all .15s;
}

.comment__sort__item__active {
  background-color: black;
  color: white;
  font-weight: 600;
}
</style>